﻿using System.Collections.Generic;

namespace Raven
{
    public class FuzzyVariable
    {
        private Dictionary<string, FuzzySet> _memberSets = new Dictionary<string, FuzzySet>();

        private Fix _minRange;
        private Fix _maxRange;

        private void AdjustRangeToFit(Fix min, Fix max)
        {
            if (min < _minRange) _minRange = min;
            if (max > _maxRange) _maxRange = max;
        }


        public FuzzyVariable()
        {
            _minRange = Fix.Zero;
            _maxRange = Fix.Zero;
        }

        public FzSet AddLeftShoulderSet(string name, Fix minBound, Fix peak, Fix maxBound)
        {
            _memberSets[name] = new FuzzySetLeftShoulder(peak, peak - minBound, maxBound - peak);
            AdjustRangeToFit(minBound, maxBound);
            return new FzSet(_memberSets[name]);
        }

        public FzSet AddRightShoulderSet(string name, Fix minBound, Fix peak, Fix maxBound)
        {
            _memberSets[name] = new FuzzySetRightShoulder(peak, peak - minBound, maxBound - peak);
            AdjustRangeToFit(minBound, maxBound);
            return new FzSet(_memberSets[name]);
        }

        public FzSet AddTriangularSet(string name, Fix minBound, Fix peak, Fix maxBound)
        {
            _memberSets[name] = new FuzzySetTriangle(peak, peak - minBound, maxBound - peak);
            AdjustRangeToFit(minBound, maxBound);
            return new FzSet(_memberSets[name]);
        }

        public FzSet AddSingletonSet(string name, Fix minBound, Fix peak, Fix maxBound)
        {
            _memberSets[name] = new FuzzySetSingleton(peak, peak - minBound, maxBound - peak);
            AdjustRangeToFit(minBound, maxBound);
            return new FzSet(_memberSets[name]);
        }

        public void Fuzzify(Fix val)
        {
            Utility.Assert(val >= _minRange && val <= _maxRange, "<FuzzyVariable::Fuzzify>: value out of range");
            foreach (var memberSet in _memberSets)
            {
                memberSet.Value.SetDOM(memberSet.Value.CalculateDOM(val));
            }
        }

        public Fix DeFuzzifyMaxAv()
        {
            Fix bottom = 0;
            Fix top = 0;

            foreach (var memberSet in _memberSets)
            {
                bottom += memberSet.Value.GetDOM();
                top += memberSet.Value.GetRepresentVal() * memberSet.Value.GetDOM();
            }

            if (bottom == 0)
            {
                return 0;
            }

            return top / bottom;
        }
    }
}